/*
@Time : 2020-01-13 11:55
@Author : wj
@File : token
@Software: GoLand
*/

package token

import (
	"errors"
	"fmt"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
)

var (
	ErrMissingHeader = errors.New("请求头`Authorization`为空.")
)

// Context是JSON Web令牌的上下文
type Context struct {
	ID       uint64
	Username string
}

func secretFunc(secret string) jwt.Keyfunc {
	return func(token *jwt.Token) (i interface{}, e error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, jwt.ErrSignatureInvalid
		}

		return []byte(secret), nil
	}
}

//解析使用指定的机密验证token
func Parse(tokenString string, secret string) (*Context, error) {
	cxt := &Context{}

	//解析token
	token, err := jwt.Parse(tokenString, secretFunc(secret))

	if err != nil {
		return cxt, err

	} else if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		// 如果令牌有效就设置令牌内容
		cxt.ID = uint64(claims["id"].(float64))
		cxt.Username = claims["username"].(string)
		return cxt, nil
	} else {
		return cxt, err
	}

}

// 获取令牌并解析
func ParseRequest(c *gin.Context) (*Context, error) {
	header := c.Request.Header.Get("Authorization")

	// 从配置加载jwt_secret
	secret := viper.GetString("jwt_secret")

	if len(header) == 0 {
		return &Context{}, ErrMissingHeader
	}

	var t string
	fmt.Sscanf(header, "Bearer %s", &t)
	return Parse(t, secret)
}

// 使用指定的秘密对上下文进行签名
func Sign(ctx *gin.Context, c Context, secret string) (tokenString string, err error) {
	// 如果未指定密钥，则从Gin配置中加载jwt密钥
	if secret == "" {
		secret = viper.GetString("jwt_secret")
	}
	// token内容
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"id":       c.ID,
		"username": c.Username,
		"nbf":      time.Now().Unix(),
		"iat":      time.Now().Unix(),
	})
	// token签名
	tokenString, err = token.SignedString([]byte(secret))

	return
}
